﻿using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;

using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Modes;
using Org.BouncyCastle.Crypto.Parameters;

namespace Utils.Security
{
    public class AESHelper
    {

        public static string DecryptByPKCS7CBC(string text, string AesKey, string AesIV)
        {
            try
            {
                byte[] encryptedData = Convert.FromBase64String(text);  // strToToHexByte(text);
                RijndaelManaged rijndaelCipher = new RijndaelManaged();
                rijndaelCipher.Mode = CipherMode.CBC;
                rijndaelCipher.Padding = PaddingMode.PKCS7;
                using (ICryptoTransform transform = rijndaelCipher.CreateDecryptor(Convert.FromBase64String(AesKey), Convert.FromBase64String(AesIV)))
                {
                    byte[] plainText = transform.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
                    string result = Encoding.UTF8.GetString(plainText);
                    return result;
                }
            }
            catch (Exception)
            {
                throw new Exception("iv已失效");
            }
        }

        public static string AesGcmDecrypt(string aesKey, string associatedData, string nonce, string ciphertext)
        {
            GcmBlockCipher gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            AeadParameters aeadParameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(aesKey)),
                128,
                Encoding.UTF8.GetBytes(nonce),
                Encoding.UTF8.GetBytes(associatedData));
            gcmBlockCipher.Init(false, aeadParameters);

            byte[] data = Convert.FromBase64String(ciphertext);
            byte[] plaintext = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
            int length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, plaintext, 0);
            gcmBlockCipher.DoFinal(plaintext, length);
            return Encoding.UTF8.GetString(plaintext);
        }
        public static string AesGcmEncrypt(string aesKey, string associatedData, string nonce, string ciphertext)
        {
            var associatedBytes = associatedData == null ? null : Encoding.UTF8.GetBytes(associatedData);

            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());
            var parameters = new AeadParameters(
                new KeyParameter(Encoding.UTF8.GetBytes(aesKey)),
                128, //128 = 16 * 8 => (tag size * 8)
                Encoding.UTF8.GetBytes(nonce),
                associatedBytes);

            gcmBlockCipher.Init(true, parameters);

            var data = Encoding.UTF8.GetBytes(ciphertext);
            var cipherData = new byte[gcmBlockCipher.GetOutputSize(data.Length)];
            var length = gcmBlockCipher.ProcessBytes(data, 0, data.Length, cipherData, 0);
            gcmBlockCipher.DoFinal(cipherData, length);
            return Convert.ToBase64String(cipherData);
        }


        public static string EncryptAesGcmNoPadding(string aesKey, string nonce, string plaintext)
        {
            var keyParam = new KeyParameter(Encoding.UTF8.GetBytes(aesKey));
            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());

            var nonceBytes = Encoding.UTF8.GetBytes(nonce);
            // 初始化GCM
            var parameters = new AeadParameters(keyParam, 128, nonceBytes, null);
            gcmBlockCipher.Init(true, parameters);

            var plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
            var ciphertextBytes = new byte[gcmBlockCipher.GetOutputSize(plaintextBytes.Length)];

            // 执行加密
            var length = gcmBlockCipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, ciphertextBytes, 0);
            gcmBlockCipher.DoFinal(ciphertextBytes, length);
            return Convert.ToBase64String(ciphertextBytes);
        }


        public static string DecryptAesGcmNoPadding(string aesKey, string nonce, string plaintext)
        {
            var keyParam = new KeyParameter(Encoding.UTF8.GetBytes(aesKey));
            var gcmBlockCipher = new GcmBlockCipher(new AesEngine());

            var nonceBytes = Encoding.UTF8.GetBytes(nonce);

            var parameters = new AeadParameters(keyParam, 128, nonceBytes, null);
            gcmBlockCipher.Init(false, parameters);

            var plaintextBytes = Convert.FromBase64String(plaintext);
            var ciphertextBytes = new byte[gcmBlockCipher.GetOutputSize(plaintextBytes.Length)];

            var length = gcmBlockCipher.ProcessBytes(plaintextBytes, 0, plaintextBytes.Length, ciphertextBytes, 0);
            gcmBlockCipher.DoFinal(ciphertextBytes, length);
            return Encoding.UTF8.GetString(ciphertextBytes);
        }
    }
}
